// Tencent is pleased to support the open source community by making RapidJSON
// available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All
// rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License
// at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

#ifndef RAPIDJSON_INTERNAL_META_H_
#define RAPIDJSON_INTERNAL_META_H_

#include "../rapidjson.h"

#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif

#if defined(_MSC_VER) && !defined(__clang__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(6334)
#endif

#if RAPIDJSON_HAS_CXX11_TYPETRAITS
#include <type_traits>
#endif

//@cond RAPIDJSON_INTERNAL
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {

// Helper to wrap/convert arbitrary types to void, useful for arbitrary type
// matching
template <typename T>
struct Void {
  typedef void Type;
};

///////////////////////////////////////////////////////////////////////////////
// BoolType, TrueType, FalseType
//
template <bool Cond>
struct BoolType {
  static const bool Value = Cond;
  typedef BoolType Type;
};
typedef BoolType<true> TrueType;
typedef BoolType<false> FalseType;

///////////////////////////////////////////////////////////////////////////////
// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
//

template <bool C>
struct SelectIfImpl {
  template <typename T1, typename T2>
  struct Apply {
    typedef T1 Type;
  };
};
template <>
struct SelectIfImpl<false> {
  template <typename T1, typename T2>
  struct Apply {
    typedef T2 Type;
  };
};
template <bool C, typename T1, typename T2>
struct SelectIfCond : SelectIfImpl<C>::template Apply<T1, T2> {};
template <typename C, typename T1, typename T2>
struct SelectIf : SelectIfCond<C::Value, T1, T2> {};

template <bool Cond1, bool Cond2>
struct AndExprCond : FalseType {};
template <>
struct AndExprCond<true, true> : TrueType {};
template <bool Cond1, bool Cond2>
struct OrExprCond : TrueType {};
template <>
struct OrExprCond<false, false> : FalseType {};

template <typename C>
struct BoolExpr : SelectIf<C, TrueType, FalseType>::Type {};
template <typename C>
struct NotExpr : SelectIf<C, FalseType, TrueType>::Type {};
template <typename C1, typename C2>
struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
template <typename C1, typename C2>
struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {};

///////////////////////////////////////////////////////////////////////////////
// AddConst, MaybeAddConst, RemoveConst
template <typename T>
struct AddConst {
  typedef const T Type;
};
template <bool Constify, typename T>
struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
template <typename T>
struct RemoveConst {
  typedef T Type;
};
template <typename T>
struct RemoveConst<const T> {
  typedef T Type;
};

///////////////////////////////////////////////////////////////////////////////
// IsSame, IsConst, IsMoreConst, IsPointer
//
template <typename T, typename U>
struct IsSame : FalseType {};
template <typename T>
struct IsSame<T, T> : TrueType {};

template <typename T>
struct IsConst : FalseType {};
template <typename T>
struct IsConst<const T> : TrueType {};

template <typename CT, typename T>
struct IsMoreConst
    : AndExpr<
          IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
          BoolType<IsConst<CT>::Value >= IsConst<T>::Value>>::Type {};

template <typename T>
struct IsPointer : FalseType {};
template <typename T>
struct IsPointer<T *> : TrueType {};

///////////////////////////////////////////////////////////////////////////////
// IsBaseOf
//
#if RAPIDJSON_HAS_CXX11_TYPETRAITS

template <typename B, typename D>
struct IsBaseOf : BoolType<::std::is_base_of<B, D>::value> {};

#else  // simplified version adopted from Boost

template <typename B, typename D>
struct IsBaseOfImpl {
  RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
  RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);

  typedef char (&Yes)[1];
  typedef char (&No)[2];

  template <typename T>
  static Yes Check(const D *, T);
  static No Check(const B *, int);

  struct Host {
    operator const B *() const;
    operator const D *();
  };

  enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) };
};

template <typename B, typename D>
struct IsBaseOf : OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D>>>::Type {};

#endif  // RAPIDJSON_HAS_CXX11_TYPETRAITS

//////////////////////////////////////////////////////////////////////////
// EnableIf / DisableIf
//
template <bool Condition, typename T = void>
struct EnableIfCond {
  typedef T Type;
};
template <typename T>
struct EnableIfCond<false, T> { /* empty */
};

template <bool Condition, typename T = void>
struct DisableIfCond {
  typedef T Type;
};
template <typename T>
struct DisableIfCond<true, T> { /* empty */
};

template <typename Condition, typename T = void>
struct EnableIf : EnableIfCond<Condition::Value, T> {};

template <typename Condition, typename T = void>
struct DisableIf : DisableIfCond<Condition::Value, T> {};

// SFINAE helpers
struct SfinaeTag {};
template <typename T>
struct RemoveSfinaeTag;
template <typename T>
struct RemoveSfinaeTag<SfinaeTag &(*)(T)> {
  typedef T Type;
};

#define RAPIDJSON_REMOVEFPTR_(type)                          \
  typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag< \
      ::RAPIDJSON_NAMESPACE::internal::SfinaeTag &(*)type>::Type

#define RAPIDJSON_ENABLEIF(cond)                                            \
  typename ::RAPIDJSON_NAMESPACE::internal::EnableIf<RAPIDJSON_REMOVEFPTR_( \
      cond)>::Type * = NULL

#define RAPIDJSON_DISABLEIF(cond)                                            \
  typename ::RAPIDJSON_NAMESPACE::internal::DisableIf<RAPIDJSON_REMOVEFPTR_( \
      cond)>::Type * = NULL

#define RAPIDJSON_ENABLEIF_RETURN(cond, returntype)   \
  typename ::RAPIDJSON_NAMESPACE::internal::EnableIf< \
      RAPIDJSON_REMOVEFPTR_(cond), RAPIDJSON_REMOVEFPTR_(returntype)>::Type

#define RAPIDJSON_DISABLEIF_RETURN(cond, returntype)   \
  typename ::RAPIDJSON_NAMESPACE::internal::DisableIf< \
      RAPIDJSON_REMOVEFPTR_(cond), RAPIDJSON_REMOVEFPTR_(returntype)>::Type

}  // namespace internal
RAPIDJSON_NAMESPACE_END
//@endcond

#if defined(_MSC_VER) && !defined(__clang__)
RAPIDJSON_DIAG_POP
#endif

#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif

#endif  // RAPIDJSON_INTERNAL_META_H_
